﻿/*************************************************
  Copyright (C), 2014-2017, lmd Tech. Co., Ltd.
  文 件  名:	Assets\Scripts\Tools\FSM\CLStateMachine.cs
  作       者:	陈佳
  版       本:	1.0        
  完成日期:	2017//      
  功能描述:	一个轻量的状态机,提供一个类的脚本使用
  主要功能: 
*************************************************/
//#define DEBUG_CLSTATE
using System;
using System.Collections.Generic;
using TaskFunc = System.Func<float, bool>;
using UnityEngine;

namespace CxExtension
{
	public class CLStateMachine : CLState
	{
		private Dictionary<string,CLState> m_states = new Dictionary<string,CLState>();     //状态机map

		private bool m_needChange = false;                                                                        //是否需要改变状态
		private bool m_ignoreToSelf = true;                                                                         //忽略相同状态的切换,比如从a状态切换到a状态 	

		private CLState m_curState;                                                                                     //当前状态名称
		private string m_nextStateName;                                                                             //下一个状态的名称

		public CLState curState
		{
			get
			{
				return m_curState;
			}
		}

		/// <summary>
		/// 添加一个状态
		/// </summary>
		/// <param name="state"></param>
		public void AddState(CLState state)
		{
			if(state == null)
			{
				Debuger.ErrFormat("状态不能为null");
				return;
			}
			if(m_states.ContainsKey(state.Name))
			{
				Debuger.ErrFormat("重复添加状态:{0}",state.Name);
			}
			else
			{
				m_states.Add(state.Name,state);
			}
		}

		public void AddState<T>(T stateId, Action enter, TaskFunc update, Action leave = null)
		{
			var stname = stateId.ToString();
			var state = new CLState(stname, enter, update, leave);
			AddState(state);
		}

		/// <summary>
		/// 通过名称改变状态
		/// </summary>
		/// <param name="name"></param>
		public void ChangeState(string name)
		{
			if(string.IsNullOrEmpty(name))
			{
				Debuger.WrnFormat("状态名不能为空:{0}",name);
				return;
			}
			m_needChange = true;
			m_nextStateName = name;
		}

		public void ChangeStateByT<T>(T stateId)
		{
			var stateName = stateId.ToString();
			ChangeState(stateName);
		}

		/// <summary>
		/// 通过名称获取状态
		/// </summary>
		/// <param name="name"></param>
		/// <returns></returns>
		public CLState GetState(string name)
		{
			CLState state = null;
			if(string.IsNullOrEmpty(name))
			{
				Debuger.WrnFormat("状态名不能为空:{0}",name);
				return state;
			}
			if(m_states.TryGetValue(name,out state) == false)
			{
				Debuger.WrnFormat("状态不存在:{0}",name);
			}
			return state;
		}

		// Update is called once per frame
		public bool Update(float dt)
		{
			base.OnUpdate(dt);
			return updateChildState(dt);
		}


		/// <summary>
		/// 清除状态,把当前状态设置为null,退出的时候会调用
		/// </summary>
		public void ClearState()
		{
			changeStateInstantly(null);
		}

		/// <summary>
		/// 退出状态时调用此函数 ,会调用状态离开以及一些清理
		/// </summary>
		public void Exit()
		{
			OnLeave();
		}

		/// <summary>
		/// 状态退出时候调用,不需要使用者调用
		/// </summary>
		public override void OnLeave()
		{
			base.OnLeave();
			ClearState();
		}

		/// <summary>
		/// 处理子状态的逻辑
		/// </summary>
		/// <param name="dt"></param>
		/// <returns></returns>
		private bool updateChildState(float dt)
		{
			if(m_needChange)
			{
				var nextState = GetState(m_nextStateName);

				//如果时相同状态
				if(m_ignoreToSelf && nextState == m_curState)
				{
#if DEBUG_CLSTATE
					string stName = m_curState == null ? "null" : m_curState.Name;
					Debuger.LogFormat("相同状态不转换:{0},{1}",stName,Time.realtimeSinceStartup);
#endif
				}
				else
				{
					changeStateInstantly(nextState);
				}
				m_needChange = false;
			}
			if(m_curState != null)
			{
				return m_curState.OnUpdate(dt);
			}
			return true;
		}

		/// <summary>
		/// 立即切换状态
		/// </summary>
		/// <param name="nextState"></param>
		private void changeStateInstantly(CLState nextState)
		{
			if(m_curState != null)
			{
#if DEBUG_CLSTATE
				Debuger.LogFormat("离开状态:{0},{1}",m_curState.Name,Time.realtimeSinceStartup);
#endif
				m_curState.OnLeave();
			}
			//切换下一个状态
			m_curState = nextState;
			if(m_curState != null)
			{
#if DEBUG_CLSTATE
				Debuger.LogFormat("进入状态:{0},{1}",m_curState.Name,Time.realtimeSinceStartup);
#endif
				m_curState.OnEnter();
			}
		}
	}
}
